//----------------------------------------------------
//
// SharePoint Web Part Visibility Filter Web Part
//
// 08/01/2009 - Initial release to public
//
//----------------------------------------------------
using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Serialization;
using System.Security;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.WebPartPages.Communication;

using Microsoft.Office.Server.UserProfiles;
using Microsoft.Office.Server;

using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Data;
using System.Collections.Specialized;

namespace SPVisibilityFilter
{
    /// <summary>
    /// SPVisibilityFilter - web part that selectively hides or unhides other web parts
    /// </summary>
    [DefaultProperty("URL"),
        ToolboxData("<{0}:SPVisibilityFilter runat=server></{0}:SPVisibilityFilter>"),
        XmlRoot(Namespace = "SPVisibilityFilter"),
        Obsolete]
    public class SPVisibilityFilter : Microsoft.SharePoint.WebPartPages.WebPart, ICellConsumer
    {
        public event CellConsumerInitEventHandler CellConsumerInit;

        // Web part configuration
        public enum DataSource { Connection, UserProfile, QueryString };
        public enum Fields { Title, Description, HelpUrl, TitleUrl, ZoneID, ZoneIndex };
        public enum CompareOps { Equals, NotEquals, Contains, DoesNotContain, Matches, NotMatches }
        public enum Visibility { Hide, Show }

        private DataSource _source;
        private string _sourceValue;
        private CompareOps _sourceCompareOp;
        private string _connectionValue;
        private Fields _webPartField;
        private CompareOps _compareOp;
        private string _compareValue;
        private Visibility _webPartVisibility;
        private bool _changeNonMatchingParts;

        private bool requiresTitleMods = false;
        StringCollection webPartsToMod;

        // Javascript code to remove "(Hidden)" from web part titles if the web part was initially hidden
        private string titleModScriptStart =
            @"<script type=""text/javascript"">
                function SPVFReplaceTitle(webPartID)
                {
                    var SPVFobj;
                    var SPVFtitle;

                    SPVFobj = document.getElementById(webPartID);
                    if (SPVFobj)
                    {
                        SPVFobj.title = SPVFobj.title.replace(/\(Hidden\)./g,"""");
                        SPVFobj.innerHTML = SPVFobj.innerHTML.replace(/.\(Hidden\)./g,"""");
                    }
                }
                function SPVFReplaceHidden()
                {";
        private string titleModScriptCall = @"SPVFReplaceTitle(""{0}"");";
        private string titleModScriptEnd = 
            @"  }
            _spBodyOnLoadFunctionNames.push(""SPVFReplaceHidden"");
            </script>";


        // Cell connection information
        string cellName = "Cell Data";
        string cellValue = "";

        protected bool isConnected = false;

        // Web Part properties
        [Browsable(true),
            Category("Trigger"),
            DefaultValue(DataSource.Connection),
            WebPartStorage(Storage.Shared),
            FriendlyName("Data Source"),
            Description("Source for data to determine whether or not to hide/show web parts.")]
        public DataSource Source
        {
            get
            {
                return _source;
            }

            set
            {
                _source = value;
            }
        }

        [Browsable(true),
            Category("Trigger"),
            DefaultValue(""),
            WebPartStorage(Storage.Shared),
            FriendlyName("Data Source Field"),
            Description("Field in the Data Source to get a value from (i.e. User Profile property, Query String field. Not used if Connection.")]
        public string DataSourceValue
        {
            get
            {
                return _sourceValue;
            }

            set
            {
                _sourceValue = value;
            }
        }

        [Browsable(true),
            Category("Trigger"),
            DefaultValue(CompareOps.Equals),
            Description("The type of comparison to use on the Data Source value"),
            FriendlyName("Data Source Comparison Type"),
            WebPartStorageAttribute(Storage.Shared)]
        public CompareOps DataSourceCompareOp
        {
            get
            {
                return _sourceCompareOp;
            }

            set
            {
                _sourceCompareOp = value;
            }
        }


        [Browsable(true),
            Category("Trigger"),
            DefaultValue(""),
            WebPartStorage(Storage.Shared),
            FriendlyName("Compare Data Source Value to"),
            Description("Compare to value from the Data Source.")]
        public string ConnectionValue
        {
            get
            {
                return _connectionValue;
            }

            set
            {
                _connectionValue = value;
            }
        }

        [Browsable(true),
            Category("Web Part Selection"),
            DefaultValue(Fields.Title),
            WebPartStorageAttribute(Storage.Shared),
            FriendlyName("Compare Web Part Field"),
            Description("The field name of the web part to compare.")]
        public Fields WebPartField
        {
            get
            {
                return _webPartField;
            }

            set
            {
                _webPartField = value;
            }
        }

        [Browsable(true),
            Category("Web Part Selection"),
            DefaultValue(CompareOps.Equals),
            Description("The type of comparison on the values"),
            FriendlyName("Web Part Field Comparison Type"),
            WebPartStorageAttribute(Storage.Shared)]
        public CompareOps CompareOp
        {
            get
            {
                return _compareOp;
            }

            set
            {
                _compareOp = value;
            }
        }

        [Browsable(true),
            Category("Web Part Selection"),
            DefaultValue(""),
            Description("The value to compare the field to"),
            FriendlyName("Compare Web Part Field Value to"),
            WebPartStorageAttribute(Storage.Shared)]
        public string CompareValue
        {
            get
            {
                return _compareValue;
            }

            set
            {
                _compareValue = value;
            }
        }

        [Browsable(true),
           Category("Web Part Selection"),
           DefaultValue(Visibility.Hide),
           Description("Hide or Show the web parts if comparison is true"),
           FriendlyName("Do This to the Web Parts When True"),
           WebPartStorageAttribute(Storage.Shared)]
        public Visibility WebPartVisibility
        {
            get
            {
                return _webPartVisibility;
            }

            set
            {
                _webPartVisibility = value;
            }
        }

        [Browsable(true),
            Category("Web Part Selection"),
            DefaultValue(false),
            Description("Perform the opposite action (hide or show) on all web parts where the comparison is false?"),
            FriendlyName("Do the opposite action to other Web Parts?"),
            WebPartStorageAttribute(Storage.Shared)]
        public bool ChangeNonMatchingParts
        {
            get
            {
                return _changeNonMatchingParts;
            }

            set
            {
                _changeNonMatchingParts = value;
            }
        }

        /// <summary>
        ///	Register interfaces.
        /// </summary>
        [Obsolete]
        public override void EnsureInterfaces()
        {
            try
            {
                // Register the ICellConsumer interface.
                RegisterInterface("CellConsumer_WPQ_",
                    InterfaceTypes.ICellConsumer,
                    WebPart.UnlimitedConnections,
                    ConnectionRunAt.Server,
                    this,
                    "CellConsumer_WPQ_",
                    "Consumes a value from ",
                    "Consumes a value used to determine web part(s) to hide");
            }
            catch (SecurityException)
            {
            }
        }

        [Obsolete]
        public override ConnectionRunAt CanRunAt()
        {
            return ConnectionRunAt.Server;
        }

        /// <summary>
        ///	Web part communication
        /// </summary>
        [Obsolete]
        public override void PartCommunicationInit()
        {
            if (CellConsumerInit != null)
            {
                CellConsumerInitEventArgs cellConsumerInitArgs = new CellConsumerInitEventArgs();
                cellConsumerInitArgs.FieldName = cellName;
                CellConsumerInit(this, cellConsumerInitArgs);
            }
        }

        [Obsolete]
        public override InitEventArgs GetInitEventArgs(string interfaceName)
        {
            if (interfaceName == "CellConsumer_WPQ_")
            {
                EnsureChildControls();
                CellConsumerInitEventArgs cellConsumerInitArgs = new CellConsumerInitEventArgs();
                cellConsumerInitArgs.FieldName = cellName;
                return (cellConsumerInitArgs);
            }
            else
                return (null);
        }

        [Obsolete]
        public void CellProviderInit(object sender, CellProviderInitEventArgs cellProviderInitArgs)
        {
            // This is where the Consumer part can identify what type of "Cell" the Provider
            // will be sending.
        }

        [Obsolete]
        public override void PartCommunicationConnect(string interfaceName, WebPart connectedPart, string connectedInterfaceName, ConnectionRunAt runAt)
        {
            EnsureChildControls();

            // Keep track of the connection state.
            if (interfaceName == "CellConsumer_WPQ_")
                isConnected = true;
        }

        public void CellReady(object sender, CellReadyEventArgs cellReadyArgs)
        {
            if (cellReadyArgs.Cell != null)
                cellValue = cellReadyArgs.Cell.ToString();
        }

        /// <summary>
        /// Render this Web Part to the output parameter specified.
        /// </summary>
        /// <param name="output"> The HTML writer to write out to </param>
        protected override void RenderWebPart(HtmlTextWriter output)
        {
            bool haveValue = false;

            // Used to track web parts whose title needs to be modified (initially hidden)
            webPartsToMod = new StringCollection();

            switch (Source)
            {
                // Data source is QueryString - get the specified value
                case DataSource.QueryString:
                    if (DataSourceValue != null)
                        if (DataSourceValue.Trim().Length > 0)
                        {
                            try
                            {
                                cellValue = Page.Request.QueryString[DataSourceValue].ToString();
                                haveValue = true;
                            }
                            catch (Exception)
                            {
                                haveValue = false;
                            }
                        }
                    break;

                // Data source is UserProfile - get the specified value
                case DataSource.UserProfile:
                    if (DataSourceValue != null)
                        if (DataSourceValue.Trim().Length > 0)
                        {
                            try
                            {
                                cellValue = GetUserProfileValue(SPContext.Current.Web.CurrentUser.Name, DataSourceValue);
                                haveValue = true;
                            }
                            catch (Exception)
                            {
                                haveValue = false;
                            }
                        }
                    break;

                // Data source is Connection - get the specified value
                case DataSource.Connection:
                    if (isConnected && ConnectionValue != null)
                        if (ConnectionValue.Trim().Length > 0)
                            haveValue = true;
                    break;
            }

            if (haveValue)
            {
                // See if the value triggers a hide/show
                if (TestDataSource(cellValue, DataSourceCompareOp, ConnectionValue))
                    RecursiveFindWebParts(Page);

                // If it triggered a "show" of a initially hidden web part, emit the Javascript to remove
                // the "(Hidden)" from the titles
                if (requiresTitleMods)
                {
                    output.WriteLine(titleModScriptStart);

                    int len = webPartsToMod.Count;
                    string[] parts = new string[len];
                    webPartsToMod.CopyTo(parts, 0);
                    foreach (string s in parts)
                        output.WriteLine(String.Format(titleModScriptCall, s));
                    output.WriteLine(titleModScriptEnd);
                }
            }
        }

        // Recursively check every control on the page to find the web parts and see if it triggers a hide/show
        private void RecursiveFindWebParts(Control control)
        {
            bool matchingWebPart = false;
            string thisValue = "";

            // Special string: "%ConnectionValue%" allows the value received on the web part connection to be used
            // in the web part selection as the compare value.
            if (CompareValue.Contains("%ConnectionValue%"))
            {
                if (isConnected && cellValue != null)
                    thisValue = CompareValue.Replace("%ConnectionValue%", cellValue);
                else
                    thisValue = "";  // No connection value yet
            }
            else
            {
                thisValue = CompareValue;
            }

            foreach (Control c in control.Controls)
            {
                // Only want web parts, and don't touch Visibility Filter web parts
                if (c is Microsoft.SharePoint.WebPartPages.WebPart && !(c is SPVisibilityFilter))
                {
                    Microsoft.SharePoint.WebPartPages.WebPart wp = (Microsoft.SharePoint.WebPartPages.WebPart)c;

                    matchingWebPart = false;
                    switch (WebPartField)
                    {
                        case Fields.Title:
                            matchingWebPart = TestWebPart(wp.Title, CompareOp, thisValue);
                            break;
                        case Fields.Description:
                            matchingWebPart = TestWebPart(wp.Description, CompareOp, thisValue);
                            break;
                        case Fields.TitleUrl:
                            matchingWebPart = TestWebPart(wp.TitleUrl, CompareOp, thisValue);
                            break;
                        case Fields.HelpUrl:
                            matchingWebPart = TestWebPart(wp.HelpUrl, CompareOp, thisValue);
                            break;
                        case Fields.ZoneID:
                            matchingWebPart = TestWebPart(wp.ZoneID, CompareOp, thisValue);
                            break;
                        case Fields.ZoneIndex:
                            matchingWebPart = TestWebPart(wp.ZoneIndex.ToString(), CompareOp, thisValue);
                            break;
                    }
                    if (matchingWebPart)    // Do something with this web part - it meets the criteria
                    {
                        if (WebPartVisibility == Visibility.Show)   // Config says to show this web part - show if hidden
                        {
                            // If the web part is currently hidden and the chrome type is one that displays the web part title
                            // add it to the list of web parts where the title must be modified.  Sharepoint adds "(Hidden)" to these
                            // web part titles automatically and we can't stop it at the API level - it has to be removed in the browser through the DOM
                            if (wp.Hidden == true &&
                                (wp.ChromeType != System.Web.UI.WebControls.WebParts.PartChromeType.None && wp.ChromeType != System.Web.UI.WebControls.WebParts.PartChromeType.BorderOnly)) // currently hidden - need to mod title
                            {
                                requiresTitleMods = true;
                                // A little "magic" here - the wp.ClientName value has the format "varPartWPQ#" where # is a sequential number
                                // On the page, the title can be found in a TD tag with an ID of "WebPartTitleWPQ#", where "WPQ#" is the same value
                                // from wp.ClientName, except with "WebPartTitle" instead of "varPart".  It's a kludge, but I can't find a better
                                // way.  This may break in future versions of Sharepoint.
                                webPartsToMod.Add(wp.ClientName.Replace("varPart", "WebPartTitle"));
                            }
                            wp.Hidden = false;
                        }
                        else
                        {
                            wp.Hidden = true;
                        }
                    }
                    else
                        if (ChangeNonMatchingParts)     // Do the opposite action to web parts not meeting the criteria if the option is specified
                            if (WebPartVisibility == Visibility.Show)
                            {
                                wp.Hidden = true; // Opposite action
                            }
                            else
                            {
                                if (wp.Hidden == true &&
                                    (wp.ChromeType != System.Web.UI.WebControls.WebParts.PartChromeType.None && wp.ChromeType != System.Web.UI.WebControls.WebParts.PartChromeType.BorderOnly)) // currently hidden - need to mod title
                                {
                                    requiresTitleMods = true;
                                    webPartsToMod.Add(wp.ClientName.Replace("varPart", "WebPartTitle"));
                                }
                                wp.Hidden = false;  // Opposite action

                            }
                }

                if (c.HasControls())
                    RecursiveFindWebParts(c);
            }
        }

        // Get a value from the SharePoint User Profile (MOSS)
        private string GetUserProfileValue(string currentUser, string whichValue)
        {
            string profileValue = "";

            ServerContext serverContext = ServerContext.GetContext(SPContext.Current.Site);
            UserProfileManager upm = new UserProfileManager(serverContext);
            UserProfile up = upm.GetUserProfile(true);
            if (up != null)
                profileValue = up[whichValue].Value.ToString();

            return profileValue;
        }

        // Do the appropriate comparison of the data source value based on the configuration
        private bool TestDataSource(string dataSourceValue, CompareOps compareOp, string compareValue)
        {
            bool result = false;

            switch (compareOp)
            {
                case CompareOps.Equals:
                    if (dataSourceValue.ToLower() == compareValue.ToLower())
                        result = true;
                    break;
                case CompareOps.NotEquals:
                    if (dataSourceValue.ToLower() != compareValue.ToLower())
                        result = true;
                    break;
                case CompareOps.Contains:
                    if (dataSourceValue.ToLower().Contains(compareValue.ToLower()))
                        result = true;
                    break;
                case CompareOps.DoesNotContain:
                    if (!dataSourceValue.ToLower().Contains(compareValue.ToLower()))
                        result = true;
                    break;
                case CompareOps.Matches:
                    Regex pattern;
                    try
                    {
                        pattern = new Regex(compareValue);
                    }
                    catch (Exception)
                    {
                        pattern = null;
                    }
                    if (pattern != null)
                        if (pattern.IsMatch(dataSourceValue))
                            result = true;
                    break;
                case CompareOps.NotMatches:
                    Regex notPattern;
                    try
                    {
                        notPattern = new Regex(compareValue);
                    }
                    catch (Exception)
                    {
                        notPattern = null;
                    }
                    if (notPattern != null)
                        if (!notPattern.IsMatch(dataSourceValue))
                            result = true;
                    break;
                default:
                    result = false;
                    break;
            }

            return result;
        }

        // Do the appropriate comparison of the web part property based on the configuration
        private bool TestWebPart(string webPartAttribute, CompareOps compareOp, string compareValue)
        {
            bool result = false;

            switch (compareOp)
            {
                case CompareOps.Equals:
                    if (webPartAttribute.ToLower() == compareValue.ToLower())
                        result = true;
                    break;
                case CompareOps.NotEquals:
                    if (webPartAttribute.ToLower() != compareValue.ToLower())
                        result = true;
                    break;
                case CompareOps.Contains:
                    if (webPartAttribute.ToLower().Contains(compareValue.ToLower()))
                        result = true;
                    break;
                case CompareOps.DoesNotContain:
                    if (!webPartAttribute.ToLower().Contains(compareValue.ToLower()))
                        result = true;
                    break;
                case CompareOps.Matches:
                    Regex pattern;
                    try
                    {
                        pattern = new Regex(compareValue);
                    }
                    catch (Exception)
                    {
                        pattern = null;
                    }
                    if (pattern != null)
                        if (pattern.IsMatch(webPartAttribute))
                            result = true;
                    break;
                case CompareOps.NotMatches:
                    Regex notPattern;
                    try
                    {
                        notPattern = new Regex(compareValue);
                    }
                    catch (Exception)
                    {
                        notPattern = null;
                    }
                    if (notPattern != null)
                        if (!notPattern.IsMatch(webPartAttribute))
                            result = true;
                    break;
                default:
                    result = false;
                    break;
            }

            return result;
        }

    }
}
